﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNAViewerSample;

namespace XNAViewerSample.Physics
{
    public enum SBObjectType { PASSIVE, ACTIVE }

    public abstract class SoftBodyObjects
    {
        #region Fields
        private float mass;
        private SBObjectType sbObjectType;
        private bool initValid;
        protected Vector3 currPosition;
        protected Vector3 prevPosition;
        protected Quaternion currRotation;
        protected Vector3 currScale;
        protected Vector3 currVelocity;
        protected Vector3 resultantForce;

        public float Mass
        {
            get { return mass; }
            set { mass = value; }
        }

        public SBObjectType SBObjectType
        {
            get { return sbObjectType; }
            set { sbObjectType = value; }
        }

        public Vector3 CurrPosition
        {
            get { return currPosition; }
            set { currPosition = value; }
        }

        public Quaternion CurrRotation
        {
            get { return currRotation; }
            set { currRotation = value; }
        }
        public Vector3 CurrScale
        {
            get { return currScale; }
            set { currScale = value; }
        }

        public Vector3 PrevPosition
        {
            get { return prevPosition; }
            set { prevPosition = value; }
        }
        
        public Vector3 CurrVelocity
        {
            get { return currVelocity; }
            set { currVelocity = value; }
        }

        public Vector3 ResultantForce
        {
            get { return resultantForce; }
            set { resultantForce = value; }
        }

        public bool Valid
        {
            get { return initValid; }
            set { initValid = value; }
        }
        #endregion

        #region Constructor
        public SoftBodyObjects(float mass, SBObjectType sbObjectType)
        {
            this.mass = mass;
            this.currPosition = Vector3.Zero;
            this.prevPosition = currPosition;
            this.currRotation = Quaternion.Identity;
            this.currVelocity = Vector3.Zero;
            this.sbObjectType = sbObjectType;
        }
        #endregion

        #region Methods
        public void ResetForces()
        {
            this.resultantForce = Vector3.Zero;
        }

        public abstract void Update(GameTime gameTime, bool enable);
        #endregion
    }

    /// <summary>
    /// For this implementation, soft body objects are not
    /// complete models, but are parts of models. These parts
    /// are controlled by bones (and hence requires a skinned
    /// model). All dynamics will be performed on bones.
    /// Note: a model can have more than one soft body, hence
    /// the SoftBodyDynamics class will be instantiated on
    /// a per model basis.
    /// </summary>
    public sealed class SoftBodyBone : SoftBodyObjects
    {
        #region Fields
        public int boneIndex;
        public String boneName;
        public Matrix bindTransform;
        public CSkinnedModel mdl;
        #endregion 

        public SoftBodyBone(CSkinnedModel mdl, String name, float mass, SBObjectType sbObjectType)
            : base(mass, sbObjectType)
        {
            this.boneName = name;
            this.boneIndex = mdl.TagInfo.BoneIndices[name];
            this.mdl = mdl;

            this.currPosition = Vector3.Zero;
            this.currRotation = Quaternion.Identity;
            this.currScale = new Vector3(1.0f, 1.0f, 1.0f);
            this.prevPosition = currPosition;
            this.bindTransform = Matrix.Identity;
            this.Valid = false;
        }

        public override void Update(GameTime gameTime, bool enable)
        {
            if (enable)
            {
                // try a bindpose to worldpose to currRotation computation for pure rotation
                if (SBObjectType == Physics.SBObjectType.ACTIVE)
                {
                    mdl.Animation.MainPlayer.PlayerInfo.PhysicsTransforms[boneIndex] =
                        Matrix.CreateScale(currScale) *
                        Matrix.CreateFromQuaternion(currRotation) *
                        Matrix.CreateTranslation(currPosition);
                }
            }
            else
            {
                mdl.Animation.MainPlayer.PlayerInfo.PhysicsTransforms[boneIndex] =
                    Matrix.Identity;
            }
        }
    }
}
